/* Project: Gipsa-lab plugins for OpenVibe
 * AUTHORS AND CONTRIBUTORS: Andreev A., Barachant A., Congedo M., Ionescu,Gelu

 * This file is part of "Gipsa-lab plugins for OpenVibe".
 * You can redistribute it and/or modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License along with Brain Invaders. If not, see http://www.gnu.org/licenses/.*/
 
#ifndef __OpenViBEPlugins_BoxAlgorithm_TrainMDM_H__
#define __OpenViBEPlugins_BoxAlgorithm_TrainMDM_H__

#include "ovp_defines.h"
#include <openvibe/ov_all.h>
#include <toolkit/ovtk_all.h>

#include <itpp/base/algebra/eigen.h>
#include <itpp/base/algebra/inv.h>
#include <itpp/base/converters.h>
#include <itpp/stat/misc_stat.h>
#include <itpp/itsignal.h>
#include <itpp/itcomm.h>

#include <vector>
#include <cstdio>
#include <string>
#include <fstream>

#include "RClass.h"
#include "ovp_concurrency.h"

#define OVP_ClassId_BoxAlgorithm_TrainMDM 	 	OpenViBE::CIdentifier(0x78570AF5, 0x6290564D)
#define OVP_ClassId_BoxAlgorithm_TrainMDMDesc 	OpenViBE::CIdentifier(0x509751A7, 0x6D633159)

namespace OpenViBEPlugins
{
	namespace SignalProcessing
	{
		class CBoxAlgorithmTrainMDM : public OpenViBEToolkit::TBoxAlgorithm < OpenViBE::Plugins::IBoxAlgorithm >
		{
		public:

			virtual void release(void) { delete this; }

			virtual OpenViBE::boolean initialize(void);
			virtual OpenViBE::boolean uninitialize(void);
			virtual OpenViBE::boolean processInput(OpenViBE::uint32 ui32InputIndex);
			virtual OpenViBE::boolean process(void);

			itpp::mat convert(const OpenViBE::IMatrix& rMatrix);
			std::string intToString(int number);
			void saveFile();
			void autoValidation();
			OpenViBE::boolean train();
			void saveDistances();

			_IsDerivedFromClass_Final_(OpenViBEToolkit::TBoxAlgorithm < OpenViBE::Plugins::IBoxAlgorithm >, OVP_ClassId_BoxAlgorithm_TrainMDM);

		protected:

			//all signal inputs
			std::vector < OpenViBE::Kernel::IAlgorithmProxy* > m_vStreamDecoder;

			//stimulation input 1
			OpenViBE::Kernel::IAlgorithmProxy* m_pStimulationDecoderTrigger;
			OpenViBE::Kernel::TParameterHandler <const OpenViBE::IMemoryBuffer* > ip_pMemoryBufferToDecodeTrigger;
			OpenViBE::Kernel::TParameterHandler < OpenViBE::IStimulationSet* > op_pStimulationSetTrigger;

			//Stimulation output 1
			OpenViBEToolkit::TStimulationEncoder<CBoxAlgorithmTrainMDM> m_oStimulationEncoder;

			std::ofstream m_ParamFile;
			OpenViBE::boolean m_IsP300;

			OpenViBE::uint64  m_ui64STrainStimulationIdentifier;
			OpenViBE::boolean m_bStartTrain;

			vector_type<RClass*> m_vTrainingClasses;

			itpp::mat m_P1; //used only for P300, stores the mean of all P300 epochs
		};

		class CBoxAlgorithmTrainMDMListener : public OpenViBEToolkit::TBoxListener < OpenViBE::Plugins::IBoxListener >
		{
		public:

			virtual OpenViBE::boolean onInputAdded(OpenViBE::Kernel::IBox& rBox, const OpenViBE::uint32 ui32Index)
			{
				char l_sName[1024];
				OpenViBE::uint32 i;

				rBox.setInputType(ui32Index, OV_TypeId_StreamedMatrix);
				for(i=0; i<rBox.getInputCount(); i++)
				{
					sprintf(l_sName, "Input Signal %u", i+1);
					rBox.setInputName(i, l_sName);
				}

				return true;
			}

			_IsDerivedFromClass_Final_(OpenViBEToolkit::TBoxListener < OpenViBE::Plugins::IBoxListener >, OV_UndefinedIdentifier);
		};

		class CBoxAlgorithmTrainMDMDesc : public OpenViBE::Plugins::IBoxAlgorithmDesc
		{
		public:

			virtual void release(void) { }

			virtual OpenViBE::CString getName(void) const                { return OpenViBE::CString("Train MDM"); }
			virtual OpenViBE::CString getAuthorName(void) const          { return OpenViBE::CString("Anton Andreev, Alexandre Barachant"); }
			virtual OpenViBE::CString getAuthorCompanyName(void) const   { return OpenViBE::CString("Gipsa-lab"); }
			virtual OpenViBE::CString getShortDescription(void) const    { return OpenViBE::CString("This is a train classification box. Works on different modalities such as: motor imagery, P300, SSVP."); }
			virtual OpenViBE::CString getDetailedDescription(void) const { return OpenViBE::CString("Generates a parameter file that is used by the Process MDM box in the online phase. Reference: A. Barachant, S. Bonnet, M. Congedo and C. Jutten, 'Multiclass Brain-Computer Interface Classification by Riemannian Geometry,' in IEEE Transactions on Biomedical Engineering, vol. 59, no. 4, p. 920-928, 2012."); }
			//virtual OpenViBE::CString getCategory(void) const            { return OpenViBE::CString("Streaming"); }
			virtual OpenViBE::CString getCategory(void) const            { return OpenViBE::CString("Gipsa-lab"); }
			virtual OpenViBE::CString getVersion(void) const             { return OpenViBE::CString("1.0"); }
			virtual OpenViBE::CString getStockItemName(void) const       { return OpenViBE::CString("gtk-sort-ascending"); }

			virtual OpenViBE::CIdentifier getCreatedClass(void) const    { return OVP_ClassId_BoxAlgorithm_TrainMDM; }
			virtual OpenViBE::Plugins::IPluginObject* create(void)       { return new OpenViBEPlugins::SignalProcessing::CBoxAlgorithmTrainMDM; }
			virtual OpenViBE::Plugins::IBoxListener* createBoxListener(void) const               { return new CBoxAlgorithmTrainMDMListener; }
			virtual void releaseBoxListener(OpenViBE::Plugins::IBoxListener* pBoxListener) const { delete pBoxListener; }

			virtual OpenViBE::boolean getBoxPrototype(
				OpenViBE::Kernel::IBoxProto& rBoxAlgorithmPrototype) const
			{
				rBoxAlgorithmPrototype.addInput  ("Input Train Stim",		 OV_TypeId_Stimulations);
				rBoxAlgorithmPrototype.addInput  ("Input Signal Non Target", OV_TypeId_StreamedMatrix);
				rBoxAlgorithmPrototype.addInput  ("Input Signal Target",     OV_TypeId_StreamedMatrix);

				rBoxAlgorithmPrototype.addOutput ("Output stimulations",	 OV_TypeId_Stimulations);

				
				rBoxAlgorithmPrototype.addSetting("Parameters file path:",   OV_TypeId_Filename,"");
				rBoxAlgorithmPrototype.addSetting("Train stimulation", OV_TypeId_Stimulation, "OVTK_StimulationId_Train");
				rBoxAlgorithmPrototype.addSetting("Modality is P300",        OV_TypeId_Boolean, "false");
				rBoxAlgorithmPrototype.addFlag   (OpenViBE::Kernel::BoxFlag_CanAddInput);

				return true;
			}

			_IsDerivedFromClass_Final_(OpenViBE::Plugins::IBoxAlgorithmDesc, OVP_ClassId_BoxAlgorithm_TrainMDMDesc);
		};
	};
};

#endif // __OpenViBEPlugins_BoxAlgorithm_TrainMDM_H__
